refactor(bootstrap): split into configure/up, add state.json - #6
Conversation
- internal/deploy: Configure (checks + versola-tools + compose.yml) and Up (start stack, wait readiness) as separate steps, bootstrap.local calls both in sequence -- external behavior unchanged. - internal/state: replace bare 'version' file with state.json (target/version/configuredAt/migratedAt), keep reading the old format for deployments made by earlier CLI builds. - each configure run gets its own bundle-<timestamp> directory instead of reusing one fixed path -- works around a Docker Desktop bug where bind-mounting a wiped-and-recreated path can make cp fail with 'File exists' for a file that doesn't exist. - internal/docker: docker.Run/Cmd extracted out of bootstrap.go, reused by uninstall.go.
🤖 Augment PR SummarySummary: Refactors deployment bootstrap into separate configuration and startup stages while recording richer active-deployment state. Changes:
Technical Notes: Generated secrets are resolved from OpenBao before Compose runs, and local/VPS startup paths select different service sets while preserving legacy state readability. 🤖 Was this summary useful? React with 👍 or 👎 |
|
augment review |
* deploy: create the external openbao-file volume before compose up * secrets: OpenBao AppRole client + secrets login/test commands * deploy: resolve auth/central/edge secrets against OpenBao before starting the stack * feat: support vps target in configure/up (TARGET passthrough, confirmation before touching prod) * fix: stable compose project name across configure runs (was failing on redeploy) * fix: validate target in openbao credentials path, remove OpenBao volume on local uninstall * fix: uninstall only clears ~/.versola/active (was wiping all targets' OpenBao credentials) * fix: prompt for OpenBao secret-id instead of passing it as an arg; PortFree does a raw bind check even for our own container * fix: stop echoing vps admin password on every deploy; preserve deployment state across a failed redeploy; don't drop unknown OpenBao keys on write; prompt for secret-id instead of passing it as an arg; raw bind check for our own container's port * fix: don't over-check own container's port on native Docker; clean up orphaned OpenBao container on uninstall; fix stale secrets-login usage hint * fix: mask secret-id input at the terminal (was visible in scrollback/session recordings) * fix: write state.json atomically (was truncate-then-write); remove generated-secrets.env candidates after resolving (world-readable, held real secret material) * fix: restrict generated-secrets.env permissions immediately, not only after successful resolve * fix: don't abort configure when chmod on generated-secrets.env fails (root-owned files on Linux VPS)
|
augment review |
| func restrictGeneratedSecretsPerms(dir string) { | ||
| for _, service := range secretServices { | ||
| path := filepath.Join(dir, service+".generated-secrets.env") | ||
| if err := os.Chmod(path, 0o600); err != nil { |
There was a problem hiding this comment.
internal/deploy/secrets.go:44: On Linux, the tools container normally writes these files as root, so this chmod failure is expected; because Prepare creates a mode-0755 bundle directory, their usual 0644 mode leaves generated passwords readable to every local user. A failing configuration, including the documented initial VPS path, then leaves the candidate files exposed indefinitely.
Severity: high
🤖 Was this useful? React with 👍 or 👎, or 🚀 if it prevented an incident/outage.
…-exposure gap chmod couldn't (root-owned files on Linux)
|
augment review |
| // etc.) -- deleting those should be a deliberate decision someone | ||
| // makes on purpose, not a side effect of running this general | ||
| // cleanup command against the wrong target by mistake. | ||
| if target == "local" { |
There was a problem hiding this comment.
target only reflects the last configured bundle, not every namespace retained in this shared volume. After a VPS deployment is reconfigured to local, this branch removes versola-openbao-file and destroys the still-retained versola/vps/* production secrets; could we avoid treating the shared volume as local-only?
Severity: high
🤖 Was this useful? React with 👍 or 👎, or 🚀 if it prevented an incident/outage.
bootstrapused to be one function: check the machine, generate configs,start everything — no way to stop in between. That's fine locally, where
the CLI owns the database it just created, but won't work for a real
server, where the database already exists and "change the schema" has to
be a decision someone makes on purpose, not a side effect of starting a
service. Groundwork for a separate
versola migratestep ahead ofdeploying to the VPS.
internal/deploy: split intoConfigure(prerequisite checks, runsversola-tools, writes the compose file) and
Up(starts the stack,waits for readiness).
bootstrapstill calls both in sequence, soexternal behavior is unchanged — this is a pure refactor, verified by a
local deployment behaving exactly as before.
internal/state: replaces the old bareversionfile withstate.json(target, version, configuredAt, migratedAt, bundleDir), so later commands
(status/migrate/up) can tell what's actually been deployed and how far
along it got, instead of inferring it from which files happen to exist.
Deployments made by earlier CLI builds still read correctly (loadLegacy).
configurerun now gets its ownbundle-<timestamp>directoryinstead of reusing one fixed path — works around a Docker Desktop bug
where bind-mounting a wiped-and-recreated path can make a container's own
cpfail with "File exists" for a file that doesn't exist.internal/docker:Run/Cmdextracted out of bootstrap.go, reused byuninstall.go.
No behavior change for
versola bootstrap local— verified locally.